package com.woodws.mybatis.jpa.util;

import com.woodws.mybatis.jpa.Mapper;
import com.woodws.mybatis.jpa.entity.TypeSketch;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.reflection.TypeParameterResolver;

import javax.persistence.Table;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by maoxiaodong on 2016/11/9.
 */
public class ReflectUtil {

    public static Class getFirstParameterizedType(Type type) {
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type[] actualTypes = parameterizedType.getActualTypeArguments();
            if (actualTypes.length == 1) {
                Type actualType = actualTypes[0];
                if (actualType instanceof Class) {
                    return (Class) actualType;
                } else if (actualType instanceof TypeVariable) {
                    TypeVariable typeVariable = (TypeVariable) actualType;
                    System.out.print(1);
                }
            }

        } else if (type instanceof GenericArrayType) {
            GenericArrayType genericArrayType = (GenericArrayType) type;
            Type arrayType = genericArrayType.getGenericComponentType();
            return getFirstParameterizedType(arrayType);
        }
        return null;
    }

    public static TypeSketch getReturnType(Method method, Class mapperInterface) {
        TypeSketch typeSketch = new TypeSketch();
        typeSketch.setRawType(method.getReturnType());
        Type type = TypeParameterResolver.resolveReturnType(method, mapperInterface);
        if (type instanceof Class) {
            typeSketch.setOwnerType((Class) type);
        } else {
            Class ownerType = getFirstParameterizedType(type);
            typeSketch.setOwnerType(ownerType);
        }

        return typeSketch;
    }

    public static List<TypeSketch> getParameters(Method method, Class mapperInterface) {
        List<TypeSketch> typeSketchSet = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Class rawType = parameter.getType();
            TypeSketch typeSketch = new TypeSketch();
            String name;
            if (parameter.isAnnotationPresent(Param.class)) {
                Param param = parameter.getAnnotation(Param.class);
                name = param.value();
            } else {
                if (CollectionUtil.isCollection(rawType)) {
                    name = "collection";
                } else if (rawType.isArray()) {
                    name = "array";
                } else {
                    name = parameter.getName();
                }
            }

            typeSketch.setRawType(rawType);
            typeSketch.setName(name);
            Type[] types = TypeParameterResolver.resolveParamTypes(method, mapperInterface);
            if (types.length > 0) {
                Class ownerType = getFirstParameterizedType(types[0]);
                typeSketch.setOwnerType(ownerType);
            }
            typeSketchSet.add(typeSketch);
        }
        return typeSketchSet;
    }

    public static Class getEntityTypeByMapper(Class mapperInterface) {
        Type[] types = mapperInterface.getGenericInterfaces();
        for (Type type : types) {
            if (type instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Class rawType = (Class) parameterizedType.getRawType();
                if (Mapper.class.isAssignableFrom(rawType)) {
                    Class entityType = (Class) parameterizedType.getActualTypeArguments()[0];
                    if (entityType.isAnnotationPresent(Table.class)) {
                        return entityType;
                    }
                }
            }

        }
        return null;
    }
}
